using Moq;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class ProductionSyntaxErrorRecoveryFeature {
        private Mock<ISyntaxErrorRecoverer> Recoverer;
        private ParseEnvironment Environment;
        private IProduction Production;
        private string SourceString;
        private char[] Source;
        private ParseContext Context;
        private SyntaxErrorRecoveryException ExpectedRecoveryException;

        [Test]
        public void ParseShouldRecoverFromSyntaxErrorIfErrorFoundInMemoTable () {
            var result = new ParseResult (0, null, null);
            ExpectedRecoveryException = new SyntaxErrorRecoveryException (result, null);

            PrepareParserEnvironment (new FailingProduction (), true);

            try {
                Production.Parse (Source, 0, Context, SourceString, Environment);
                Assert.Fail ("expected: " + typeof (SyntaxErrorRecoveryException));
            } catch (SyntaxErrorRecoveryException rpe) {
                Assert.That (rpe, Is.SameAs (ExpectedRecoveryException));

                Assert.That (Production.GetMemoTable (Source) [0], Is.SameAs (rpe.Result));
            }
        }

        [Test]
        public void ParseShouldNotTryToRecoverIfRecovererIsAlreadyRecovering () {
            var result = new ParseResult (0, null, null);
            ExpectedRecoveryException = new SyntaxErrorRecoveryException (result, null);

            PrepareParserEnvironment (new FailingProduction (), true);
            Recoverer.SetupGet (r => r.Recovering).Returns (true);

            Assert.That (Production.Parse (Source, 0, Context, SourceString, Environment), Is.Null);
        }

        [Test]
        public void RecoveredProductionShouldClearMemo () {
            var result = new ParseResult (0, null, null);
            PrepareParserEnvironment (new RecoveredProduction (new SyntaxErrorRecoveryException (result, null)), false);

            Assert.That (
                () => { Production.Parse (Source, 0, Context, SourceString, Environment); },
                Throws.TypeOf(typeof (SyntaxErrorRecoveryException)));

            Assert.False (Production.MemoTable.ContainsKey (0));
        }

        private void PrepareParserEnvironment (IProduction production, bool setRecoveryResult) {
            Recoverer = new Mock<ISyntaxErrorRecoverer> ();
            Environment = new ParseEnvironment ("filename", Recoverer.Object);
            Production = production;

            SourceString = "source";
            Source = SourceString.ToCharArray ();
            Context = new ParseContext ();

            if (setRecoveryResult) {
                Production.GetMemoTable (Source) [0] = Environment.SyntaxErrors.ErrorResult;
            }

            Recoverer
                .Setup (r => r.Recover (Source, 0, Context, SourceString, Environment, Production))
                .Returns (ExpectedRecoveryException);
        }
    }
}